//@author A0099223B

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class MainUi {

	private static final String MESSAGE_REQUEST_USE_OF_ID_TAG = "Multiple match found! Please use %1$s /id [task id] to refer to task.";
	private static final String MESSAGE_STARTUP_NO_TASK = "No Tasks Recorded";
	private static final String MESSAGE_ACTION_UNAVAILABLE = "Action Unavailable";
	private static final String MESSAGE_POPUP_CLOSED = "Popup closed";
	private static final String MESSAGE_TASK_NOT_FOUND = "Task Not Found";
	private static final String MESSAGE_FORMAT_ACTION = "%1$s Task \"%2$s\" (ID#%3$s)%4$s";
	private static final String MESSAGE_FORMAT_REDONE = "Redone: %1$s";
	private static final String MESSAGE_FORMAT_UNDONE = "Undone: %1$s";
	private static final String MESSAGE_FORMAT_TITLE_ID = "%1$s (ID:%2$s)";
	private static final String MESSAGE_FORMAT_STARTIME = "Start Time: %1$s";
	private static final String MESSAGE_FORMAT_ENDTIME = "End Time: %1$s";
	private static final String MESSAGE_FORMAT_PLACE = "Place: %1$s";
	private static final String MESSAGE_FORMAT_PROJECT = "Project: %1$s";
	private static final String MESSAGE_FORMAT_DESCRIPTION = "Description: %1$s";
	private static final String MESSAGE_FORMAT_SHORT_TASK_FIRST_LINE = "%1$s (ID:%2$s) %3$s %4$s";
	private static final String MESSAGE_FORMAT_SHORT_TASK_SECOND_LINE = "Start: %1$s  End: %2$s  Place: %3$s";

	private static final String MESSAGE_FRAGMENT_NOT_DONE = "not done";
	private static final String MESSAGE_FRAGMENT_DONE = "done";
	private static final String MESSAGE_FRAGMENT_DELETING_OF = "Deleting of %1$s";
	private static final String MESSAGE_FRAGMENT_EDITING_OF = "Editing of %1$s";
	private static final String MESSAGE_FRAGMENT_ADDON_TITLE_AFTER_EDIT = " (Title After Edit: %1$s)";
	private static final String MESSAGE_FRAGMENT_FORMAT_TITLE = "\"%1$s\"";
	private static final String MESSAGE_FRAGMENT_ADDING_OF = "Adding of %1$s";
	private static final String MESSAGE_FRAGMENT_TASK_ID = "Task (ID:%1$s)";
	private static final String MESSAGE_FRAGMENT_ACTION_MARKED = "Marked";
	private static final String MESSAGE_FRAGMENT_ACTION_DELETED = "Deleted";
	private static final String MESSAGE_FRAGMENT_ACTION_EDITED = "Edited";
	private static final String MESSAGE_FRAGMENT_ACTION_ADDED = "Added";
	private static final String MESSAGE_FRAGMENT_DONE_MARK = "[Done]";
	
	private static final String MESSAGE_ENDING_MARKED_NOT_DONE = " as not done";
	private static final String MESSAGE_ENDING_MARKED_DONE = " as done";

	private static final String COMMAND_VIEW = "view ";
	private static final String COMMAND_DELETE = "delete ";
	private static final String COMMAND_EDIT = "edit";
	private static final String COMMAND_DONE = "done";
	
	private static final String TAG_PROJECT = "#";

	private static final String STRING_NO_VALUE = "--";
	private static final String DEFAULT_FORMAT_TIME = "dd/MM/yyyy HH:mm";

	private static final String STARTUP_LIST_COMMAND = "list";
	private static final String WINDOW_TITLE = "TaskBuddy";
	
	private static final String EMPTY_STRING = "";

	private static Logger logger = Logger.getLogger("MainUi");
	public static JFrame mainFrame;
	private static ListCommand lastListCommand = null;

	public static void display() {

		initialiseMainFrame();
		layoutComponents();
		showWindow();
		displayStartupDataAndMessages();

	}

	public static void processUserInput(String userInput) {

		Command userCommand = null;

		try {
			userCommand = Parser.parseCommand(userInput);

			try {

				logger.log(Level.INFO, "Sending to Logic");
				ExecutionReply executionReply = Logic
						.executeCommand(userCommand);
				processExecutionReply(executionReply, userCommand);

			} catch (TaskNotFoundException tnfe) {
				ConsoleManager.printMessage(MESSAGE_TASK_NOT_FOUND);
			} catch (PopupClosedException pce) {
				ConsoleManager.printMessage(MESSAGE_POPUP_CLOSED);
			} catch (NotUniqueException nue) {
				printRequestToUseId(nue.allMatches, userCommand);
			} catch (ActionUnavailableException aue) {
				ConsoleManager.printMessage(MESSAGE_ACTION_UNAVAILABLE);
			} catch (InvalidInputException iie) {
				ConsoleManager.printMessage(iie.getMessage());
			}

		} catch (InvalidInputException iie) {
			ConsoleManager.printMessage(iie.getMessage());
		}

	}

	private static void initialiseMainFrame() {
		mainFrame = new JFrame(WINDOW_TITLE);
		mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}

	private static void layoutComponents() {
		JPanel layoutPanel = new JPanel();
		layoutPanel.setLayout(new BoxLayout(layoutPanel, BoxLayout.Y_AXIS));

		JPanel taskTablePanel = TaskTableManager.getTablePanel();
		layoutPanel.add(taskTablePanel);

		JPanel consolePanel = ConsoleManager.getConsolePanel();
		layoutPanel.add(consolePanel);

		mainFrame.add(layoutPanel);

	}

	private static void showWindow() {
		mainFrame.pack();
		mainFrame.setVisible(true);
		ConsoleManager.setFocus();

	}

	private static void displayStartupDataAndMessages() {
		ConsoleManager.printMessage("Welcome to TaskBuddy!");
		getListAtStartup();
	}

	private static void getListAtStartup() {
		setDefaultLastListCommand();
		ArrayList<Task> startUpList = rerunLastListCommand();
		ifNoTasksAtStartupNotifyUser(startUpList);

	}

	private static void setDefaultLastListCommand() {
		try {
			lastListCommand = (ListCommand) Parser
					.parseCommand(STARTUP_LIST_COMMAND);
		} catch (InvalidInputException e) {
			// Do nothing, given last list command is expected to be
			// correct/parasable
		}
	}

	private static void ifNoTasksAtStartupNotifyUser(ArrayList<Task> startUpList) {
		if (startUpList.isEmpty()) {
			ConsoleManager.printMessage(MESSAGE_STARTUP_NO_TASK);
		}
	}

	private static void processExecutionReply(ExecutionReply executionReply,
			Command userCommand) {
		if (executionReply instanceof SingleTaskReply) {
			processSingleTaskReply(executionReply, userCommand);

		} else if (executionReply instanceof MultipleTasksReply) {
			processMultipleTaskReply(executionReply, userCommand);

		} else if (executionReply instanceof CommandReply) {
			processCommandReply(executionReply, userCommand);

		} else if (executionReply instanceof TextReply) {
			processTextReply(executionReply);
		}

	}

	private static void processSingleTaskReply(ExecutionReply executionReply,
			Command userCommand) {
		if (userCommand instanceof ViewCommand) {
			printTask(((SingleTaskReply) executionReply).task);
		} else {
			printAction((SingleTaskReply) executionReply, userCommand);
			rerunLastListCommand();
		}
	}

	// Returns List of Tasks from the rerun,
	// Returns Empty List if Exceptions or Null Command found
	private static ArrayList<Task> rerunLastListCommand() {
		if (lastListCommand == null) {
			return new ArrayList<Task>();
		}

		try {
			ExecutionReply executionReply = Logic
					.executeCommand(lastListCommand);

			assert (executionReply instanceof MultipleTasksReply);
			processExecutionReply(executionReply, lastListCommand);

			return ((MultipleTasksReply) executionReply).listOfTasks;

		} catch (TaskNotFoundException | PopupClosedException
				| NotUniqueException | ActionUnavailableException
				| InvalidInputException e) {
			return new ArrayList<Task>();
		}

	}

	private static void processMultipleTaskReply(ExecutionReply executionReply,
			Command userCommand) {
		setNewLastListCommand(userCommand, executionReply);
		TaskTableManager.refresh(
				((MultipleTasksReply) executionReply).listOfTasks,
				lastListCommand);
	}

	private static void setNewLastListCommand(Command userCommand,
			ExecutionReply executionReply) {
		if (userCommand instanceof ListCommand) {
			lastListCommand = (ListCommand) userCommand;
		} else if (userCommand instanceof SearchboxCommand) {
			deriveListCommandFromFilters((TasksWithFiltersReply) executionReply);
		}

	}

	private static void deriveListCommandFromFilters(TasksWithFiltersReply reply) {
		ListCommand listCommand = new ListCommand();

		listCommand.titleFilter = reply.taskFilters.title;
		listCommand.startTimeFilter = reply.taskFilters.startTime;
		listCommand.endTimeFilter = reply.taskFilters.endTime;
		listCommand.placeFilter = reply.taskFilters.place;
		listCommand.projectFilter = reply.taskFilters.project;
		listCommand.descriptionFilter = reply.taskFilters.description;

		lastListCommand = listCommand;
	}

	private static void processCommandReply(ExecutionReply executionReply,
			Command userCommand) {
		printCommandReplyMessage((CommandReply) executionReply, userCommand);
		rerunLastListCommand();
	}

	private static void processTextReply(ExecutionReply executionReply) {
		printLines(((TextReply) executionReply).linesOfText);
	}

	private static void printRequestToUseId(ArrayList<Task> allMatches,
			Command userCommand) {

		printShotVersionOfTasks(allMatches);
		printUseIdTag(userCommand);
	}

	private static void printShotVersionOfTasks(ArrayList<Task> allMatches) {
		ConsoleManager.printMessage(EMPTY_STRING);
		for (Task task : allMatches) {
			printTaskShort(task);
			ConsoleManager.printMessage(EMPTY_STRING);
		}
	}

	private static void printTaskShort(Task task) {
		printFirstLine(task);
		printSecondLine(task);

	}

	private static void printFirstLine(Task task) {
		String doneString = getDoneString(task);
		String projectString = getProjectString(task);

		ConsoleManager.printMessage(String.format(
				MESSAGE_FORMAT_SHORT_TASK_FIRST_LINE, task.title, task.id,
				doneString, projectString));
	}

	private static String getProjectString(Task task) {
		String projectString = EMPTY_STRING;
		if (task.project != Task.EMPTY_STRING) {
			projectString = TAG_PROJECT + task.project;
		}

		return projectString;
	}

	private static String getDoneString(Task task) {
		String doneString = EMPTY_STRING;
		if (task.isDone) {
			doneString = MESSAGE_FRAGMENT_DONE_MARK;
		}
		return doneString;
	}

	private static void printSecondLine(Task task) {
		String startTime = getStartTimeString(task);
		String endTime = getEndTimeString(task);
		String place = getPlaceString(task);
		ConsoleManager.printMessage(String.format(
				MESSAGE_FORMAT_SHORT_TASK_SECOND_LINE, startTime, endTime,
				place));
	}

	private static String getStartTimeString(Task task) {
		String startTime = STRING_NO_VALUE;
		if (task.startTime != Task.EMPTY_TIME) {
			startTime = new SimpleDateFormat(DEFAULT_FORMAT_TIME)
					.format(task.startTime);
		}
		return startTime;
	}

	private static String getEndTimeString(Task task) {
		String endTime = STRING_NO_VALUE;
		if (task.endTime != Task.EMPTY_TIME) {
			endTime = new SimpleDateFormat(DEFAULT_FORMAT_TIME)
					.format(task.endTime);
		}

		return endTime;
	}

	private static String getPlaceString(Task task) {
		String place = STRING_NO_VALUE;

		if (task.place != Task.EMPTY_STRING) {
			place = task.place;
		}

		return place;
	}

	private static void printUseIdTag(Command userCommand) {
		String command = getCommandString(userCommand);

		ConsoleManager.printMessage(String.format(
				MESSAGE_REQUEST_USE_OF_ID_TAG, command));
	}

	private static String getCommandString(Command command) {
		assert (command instanceof EditCommand
				|| command instanceof DeleteCommand
				|| command instanceof ViewCommand || command instanceof DoneCommand);

		if (command instanceof EditCommand) {
			return COMMAND_EDIT;
		} else if (command instanceof DeleteCommand) {
			return COMMAND_DELETE;
		} else if (command instanceof ViewCommand) {
			return COMMAND_VIEW;
		} else if (command instanceof DoneCommand) {
			return COMMAND_DONE;
		}

		// Should not reach here
		return EMPTY_STRING;
	}

	private static void printLines(ArrayList<String> linesOfText) {
		for (String line : linesOfText) {
			ConsoleManager.printMessage(line);
		}

	}

	private static void printCommandReplyMessage(CommandReply commandReply,
			Command userCommand) {
		assert (userCommand instanceof UndoCommand || userCommand instanceof RedoCommand);

		String message = EMPTY_STRING;
		if (userCommand instanceof UndoCommand) {
			message = MESSAGE_FORMAT_UNDONE;
		} else {
			message = MESSAGE_FORMAT_REDONE;
		}

		message = String.format(message,
				stringForAffectedAction(commandReply.command));

		ConsoleManager.printMessage(message);
	}

	private static Object stringForAffectedAction(Command affectedCommand) {
		assert (affectedCommand instanceof AddCommand
				|| affectedCommand instanceof EditCommand
				|| affectedCommand instanceof DeleteCommand || affectedCommand instanceof DoneCommand);

		if (affectedCommand instanceof AddCommand) {
			return affectedAddActionString((AddCommand) affectedCommand);

		} else if (affectedCommand instanceof EditCommand) {
			return affectedEditActionString((EditCommand) affectedCommand);

		} else if (affectedCommand instanceof DeleteCommand) {
			return affectedDeleteActionString((DeleteCommand) affectedCommand);

		} else if (affectedCommand instanceof DoneCommand) {
			return affectedDoneActionString((DoneCommand) affectedCommand);
		}

		// Should not reach here
		return EMPTY_STRING;

	}

	private static Object affectedAddActionString(AddCommand affectedCommand) {

		String taskReference = obtainTaskReferenceString(affectedCommand);
		return String.format(MESSAGE_FRAGMENT_ADDING_OF, taskReference);
	}

	private static String obtainTaskReferenceString(AddCommand affectedCommand) {
		if (affectedCommand.isGuiOpenCommand) {
			return String.format(MESSAGE_FRAGMENT_TASK_ID,
					affectedCommand.taskid);
		} else {
			return String.format(MESSAGE_FRAGMENT_FORMAT_TITLE,
					affectedCommand.task.title);
		}
	}

	private static Object affectedEditActionString(EditCommand affectedCommand) {
		String taskReference = obtainTaskReferenceString(affectedCommand);
		return String.format(MESSAGE_FRAGMENT_EDITING_OF, taskReference);
	}

	private static String obtainTaskReferenceString(EditCommand affectedCommand) {
		if (affectedCommand.taskTitle != Task.EMPTY_STRING) {

			String taskReference = String.format(MESSAGE_FRAGMENT_FORMAT_TITLE,
					affectedCommand.taskTitle);

			if (affectedCommand.isEditingTitle) {
				taskReference += String.format(
						MESSAGE_FRAGMENT_ADDON_TITLE_AFTER_EDIT,
						affectedCommand.newTaskDetails.title);
			}

			return taskReference;
		} else {
			return String.format(MESSAGE_FRAGMENT_TASK_ID,
					affectedCommand.taskId);
		}
	}

	private static Object affectedDeleteActionString(
			DeleteCommand affectedCommand) {
		String taskReference = obtainTaskReferenceString(affectedCommand);
		return String.format(MESSAGE_FRAGMENT_DELETING_OF, taskReference);
	}

	private static String obtainTaskReferenceString(
			DeleteCommand affectedCommand) {
		if (affectedCommand.taskTitle != Task.EMPTY_STRING) {
			return String.format(MESSAGE_FRAGMENT_FORMAT_TITLE,
					affectedCommand.taskTitle);
		} else {
			return String.format(MESSAGE_FRAGMENT_TASK_ID,
					affectedCommand.taskId);
		}
	}

	private static Object affectedDoneActionString(DoneCommand affectedCommand) {
		String taskReference = obtainTaskReferenceString(affectedCommand);

		String originalResult = obtainOriginalResultString(affectedCommand);

		return String.format("Marking of %1$s as %2$s", taskReference,
				originalResult);
	}

	private static String obtainTaskReferenceString(DoneCommand affectedCommand) {
		if (affectedCommand.taskTitle != Task.EMPTY_STRING) {
			return String.format(MESSAGE_FRAGMENT_FORMAT_TITLE,
					affectedCommand.taskTitle);
		} else {
			return String.format(MESSAGE_FRAGMENT_TASK_ID,
					affectedCommand.taskId);
		}
	}

	private static String obtainOriginalResultString(DoneCommand affectedCommand) {
		if (affectedCommand.isDone) {
			return MESSAGE_FRAGMENT_DONE;
		} else {
			return MESSAGE_FRAGMENT_NOT_DONE;
		}
	}

	private static void printAction(SingleTaskReply reply, Command userCommand) {
		String action = obtainActionString(userCommand);
		String msgEnding = obtainMessageEnding(userCommand);

		ConsoleManager.printMessage(String.format(MESSAGE_FORMAT_ACTION,
				action, reply.task.title, reply.task.id, msgEnding));

	}

	private static String obtainActionString(Command userCommand) {
		assert (userCommand instanceof AddCommand
				|| userCommand instanceof EditCommand
				|| userCommand instanceof DeleteCommand || userCommand instanceof DoneCommand);

		if (userCommand instanceof AddCommand) {
			return MESSAGE_FRAGMENT_ACTION_ADDED;
		} else if (userCommand instanceof EditCommand) {
			return MESSAGE_FRAGMENT_ACTION_EDITED;
		} else if (userCommand instanceof DeleteCommand) {
			return MESSAGE_FRAGMENT_ACTION_DELETED;
		} else if (userCommand instanceof DoneCommand) {
			return MESSAGE_FRAGMENT_ACTION_MARKED;
		}

		// Should not reach here
		return EMPTY_STRING;
	}

	private static String obtainMessageEnding(Command userCommand) {
		if (userCommand instanceof DoneCommand) {
			if (((DoneCommand) userCommand).isDone) {
				return MESSAGE_ENDING_MARKED_DONE;
			} else {
				return MESSAGE_ENDING_MARKED_NOT_DONE;
			}
		}

		return EMPTY_STRING;
	}

	private static void printTask(Task task) {
		ConsoleManager.printMessage("");
		printTitleAndId(task);
		printIfComplete(task);

		ConsoleManager.printMessage("");
		printStartTime(task);
		printEndTime(task);
		printPlace(task);
		printProject(task);
		printDescription(task);
		ConsoleManager.printMessage("");
	}

	private static void printDescription(Task task) {

		if (task.description != Task.EMPTY_STRING) {
			ConsoleManager.printMessage("");
			ConsoleManager.printMessage(String.format(
					MESSAGE_FORMAT_DESCRIPTION, "\n" + task.description));
		} else {
			ConsoleManager.printMessage(String.format(
					MESSAGE_FORMAT_DESCRIPTION, STRING_NO_VALUE));
		}
	}

	private static void printProject(Task task) {
		String value = STRING_NO_VALUE;
		if (task.project != Task.EMPTY_STRING) {
			value = task.project;
		}
		ConsoleManager.printMessage(String
				.format(MESSAGE_FORMAT_PROJECT, value));
	}

	private static void printPlace(Task task) {
		String value = STRING_NO_VALUE;
		if (task.place != Task.EMPTY_STRING) {
			value = task.place;
		}
		ConsoleManager.printMessage(String.format(MESSAGE_FORMAT_PLACE, value));
	}

	private static void printEndTime(Task task) {
		String value = STRING_NO_VALUE;
		if (task.endTime != Task.EMPTY_TIME) {
			value = formatTime(task.endTime);
		}
		ConsoleManager.printMessage(String
				.format(MESSAGE_FORMAT_ENDTIME, value));
	}

	private static void printStartTime(Task task) {
		String value = STRING_NO_VALUE;
		if (task.startTime != Task.EMPTY_TIME) {
			value = formatTime(task.startTime);
		}
		ConsoleManager.printMessage(String.format(MESSAGE_FORMAT_STARTIME,
				value));
	}

	private static void printIfComplete(Task task) {
		if (task.isDone) {
			ConsoleManager.printMessage(MESSAGE_FRAGMENT_DONE_MARK);
		}
	}

	private static void printTitleAndId(Task task) {
		ConsoleManager.printMessage(String.format(MESSAGE_FORMAT_TITLE_ID,
				task.title, task.id));
	}

	private static String formatTime(Date time) {
		return new SimpleDateFormat(DEFAULT_FORMAT_TIME).format(time);
	}
}
